//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

package code_dao

import (
	"context"
	"fmt"

	"atomgit.com/openlinksaas/api-server/dao/db_ns"
	"atomgit.com/openlinksaas/proto-gen-go.git/project_code_api"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

type CommentDbItem struct {
	CommentId   string `bson:"_id"`
	ThreadId    string `bson:"threadId"`
	ProjectId   string `bson:"projectId"`
	ContentType uint32 `bson:"contentType"`
	Content     string `bson:"content"`
	UserId      string `bson:"userId"`
	CreateTime  int64  `bson:"createTime"`
	UpdateTime  int64  `bson:"updateTime"`
}

func (item *CommentDbItem) ToComment() *project_code_api.Comment {
	return &project_code_api.Comment{
		CommentId:   item.CommentId,
		ThreadId:    item.ThreadId,
		ContentType: project_code_api.CONTENT_TYPE(item.ContentType),
		Content:     item.Content,
		UserId:      item.UserId,
		CreateTime:  item.CreateTime,
		UpdateTime:  item.UpdateTime,
	}
}

type _CommentDao struct {
	mc *mongo.Collection
}

func newCommentDao(db *mongo.Database) (*_CommentDao, error) {
	mc := db.Collection(db_ns.DB_NS_PROJECT_CODE + "comment")
	_, err := mc.Indexes().CreateMany(context.Background(), []mongo.IndexModel{
		{
			Keys: bson.D{{"threadId", 1}},
		},
		{
			Keys: bson.D{{"projectId", 1}, {"updateTime", 1}},
		},
	})
	if err != nil {
		return nil, err
	}
	return &_CommentDao{mc: mc}, nil
}

func (db *_CommentDao) Insert(ctx context.Context, dbItem *CommentDbItem) error {
	_, err := db.mc.InsertOne(ctx, dbItem)
	return err
}

func (db *_CommentDao) Exist(ctx context.Context, projectId, commentId string) (bool, error) {
	count, err := db.mc.CountDocuments(ctx, bson.M{"projectId": projectId, "_id": commentId})
	return count > 0, err
}

func (db *_CommentDao) Get(ctx context.Context, projectId, commentId string) (*CommentDbItem, error) {
	retItem := &CommentDbItem{}
	err := db.mc.FindOne(ctx, bson.M{"projectId": projectId, "_id": commentId}).Decode(retItem)
	return retItem, err
}

func (db *_CommentDao) Remove(ctx context.Context, projectId, commentId string) error {
	_, err := db.mc.DeleteOne(ctx, bson.M{"projectId": projectId, "_id": commentId})
	return err
}

func (db *_CommentDao) UpdateContent(ctx context.Context, projectId, commentId string, contentType uint32, content string, updateTime int64) error {
	_, err := db.mc.UpdateOne(ctx, bson.M{"projectId": projectId, "_id": commentId},
		bson.M{"$set": bson.M{"contentType": contentType, "content": content, "updateTime": updateTime}})
	return err
}

func (db *_CommentDao) CountByThread(ctx context.Context, projectId, threadId string) (uint32, error) {
	count, err := db.mc.CountDocuments(ctx, bson.M{"projectId": projectId, "threadId": threadId})
	return uint32(count), err
}

func (db *_CommentDao) DistinctUserId(ctx context.Context, projectId, threadId string) ([]string, error) {
	tmpList, err := db.mc.Distinct(ctx, "userId", bson.M{"projectId": projectId, "threadId": threadId})
	if err != nil {
		return nil, err
	}
	retList := []string{}
	for _, tmpItem := range tmpList {
		retList = append(retList, tmpItem.(string))
	}
	return retList, nil
}

func (db *_CommentDao) ListByThread(ctx context.Context, projectId, threadId string) ([]*CommentDbItem, error) {
	cursor, err := db.mc.Find(ctx, bson.M{"projectId": projectId, "threadId": threadId},
		options.Find().SetSort(bson.M{"createTime": 1}))
	if err != nil {
		return nil, err
	}
	retList := []*CommentDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}

func (db *_CommentDao) ListById(ctx context.Context, commitIdList []string) ([]*CommentDbItem, error) {
	cursor, err := db.mc.Find(ctx, bson.M{"_id": bson.M{"$in": commitIdList}})
	if err != nil {
		return nil, err
	}
	retList := []*CommentDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}

func (db *_CommentDao) GetLastComment(ctx context.Context, projectId, threadId string) (*CommentDbItem, error) {
	cursor, err := db.mc.Find(ctx, bson.M{"projectId": projectId, "threadId": threadId},
		options.Find().SetLimit(1).SetSort(bson.M{"createTime": -1}))
	if err != nil {
		return nil, err
	}
	retList := []*CommentDbItem{}
	err = cursor.All(ctx, &retList)
	if err != nil {
		return nil, err
	}
	if len(retList) == 0 {
		return nil, fmt.Errorf("no comment")
	}
	return retList[0], nil
}
